#include <jni.h>
#include <string>
#include <android/log.h>
#include <pthread.h>
#include <unistd.h>

extern "C" {
#include "event_obs.h"
#include "adpcm.h"
}

#define VOICE_KEY_CODE 7
#define KEY_EVENT_TIMEOUT 1000
#define printf(f, ...) __android_log_print(ANDROID_LOG_DEBUG, "native-lib", f, ##__VA_ARGS__)

JavaVM *g_VM;
jobject jCallback;
jmethodID onBufId;
jmethodID onEventId;

int frame_difference_number = 0; //
adpcm_state state;
bool isObserverKeyEvent = false;

void *keyObserver() {
  JNIEnv *env;
  if (g_VM->GetEnv((void **) &env, JNI_VERSION_1_6) < 0) {
    if (g_VM->AttachCurrentThread(&env, NULL) < 0) {
      printf("can not Attach keyObserver Thread");
      return NULL;
    }
  }

  int ret = init_event_obs();
  if (ret < 0) {
    printf("init event obs err: %d\n", ret);
    isObserverKeyEvent = false;
    g_VM->DetachCurrentThread();
    return NULL;
  }

  Key_Event *key_event_p = (Key_Event *) calloc(1, sizeof(Key_Event));
  while (isObserverKeyEvent) {
    ret = getKeyEvent(key_event_p, KEY_EVENT_TIMEOUT);
    if (ret == 1) {
      printf("get key event : code = %d\n", key_event_p->code);
      printf("get key event : value = %d\n", key_event_p->value);
      printf("get key event : node = %s\n", key_event_p->node);
      env->CallVoidMethod(jCallback, onEventId, key_event_p->code, key_event_p->value);
    } else if(ret == 0){
      //no input event ,read time out
//      printf("no input event ,read time out\n");
    }else{
      printf("get event err %d\n", ret);
      usleep(1000);
    }
  }

  printf("destroy event obs");
  destroy_event_obs();

  g_VM->DetachCurrentThread();
  return NULL;
}



extern "C"
void decodeBuf(JNIEnv *env, char *buf, jint len) {
  //decode your buffer here
  //this example used adpcm to decode buf
  switch (buf[0]) {//<<8|buf[1]
    case 0: {
      //buffer 50~100ms audio data than call back
      //decode buffer before
      //        byte0: type = 0x0;
      //        byte1: packet index, 0~ff累加
      //        byte2: packet length, 不包含数据头
      //        byte3: 解压缩参数index
      //        byte4: 解压缩参数wPresampleH, byte5: wPresampleL
      //        byte6~byte63: encode data, 有多少填多少
      if (len < 2 || buf[2] <= 5) {
        printf("standard_hid_process buf[2] = %d \r\n", buf[2]);
        printf("standard_hid_process buf lenth is too short");
        break;
      }

      int dif_chk = buf[1] - frame_difference_number;

      if (dif_chk != 1 && dif_chk != -255) {
        printf("the frame flow number is not continuity");
        printf("buf[1] = %d frame_difference_number = %d \r\n", buf[1], frame_difference_number);
      }

      frame_difference_number = buf[1];

      state.index = buf[3];
      state.valprev = buf[4] << 8 | buf[5];

      int dataLen = buf[2] * 4;
      unsigned char decodeBuff[dataLen];
      memset(decodeBuff, 0, (size_t) dataLen);

      char *inpbuf = buf + 6;

      adpcm_decoder(inpbuf, (short *) decodeBuff, dataLen / 2, &state);

      jbyteArray jby = env->NewByteArray(dataLen);
      env->SetByteArrayRegion(jby, 0, dataLen, (const jbyte *) decodeBuff);
      env->CallVoidMethod(jCallback, onBufId, jby);
      env->DeleteLocalRef(jby);
    }
      break;
    case 1:
      env->CallVoidMethod(jCallback, onEventId, VOICE_KEY_CODE, buf[1]);
      break;
    default:
      printf("can not support process this data, buf[0] = %d ,buf[1] = %d \r\n", buf[0], buf[1]);
      break;
  }
}

extern "C"
JNIEXPORT jint JNICALL
Java_com_aispeech_astools_jni_NativeCall_registerCallbackToJni(JNIEnv *env, jclass instance, jobject cb) {
  printf("registerCallbackToJni");

  env->GetJavaVM(&g_VM);
  jCallback = env->NewGlobalRef(cb);

  if (!jCallback) {
    printf("can not get jCallback");
    return -1;
  }

  jclass jcClass = env->GetObjectClass(jCallback);
  if (!jcClass) {
    printf("can not get jcClass");
    g_VM->DetachCurrentThread();
    return -2;
  }

  //get callback method id
  onBufId = env->GetMethodID(jcClass, "onBuf", "([B)V");
  onEventId = env->GetMethodID(jcClass, "onEvent", "(II)V");

  if (!onBufId || !onEventId) {
    printf("can not get call back method");
    return -3;
  }

  pthread_t thread_readkey;
  isObserverKeyEvent = true;
  int ret = pthread_create(&thread_readkey, NULL, (void *(*)(void *)) keyObserver, NULL);
  //pthread_detach(thread_readkey);


  printf("create thread for get key event : %d \n", ret);
  return 0;
}

extern "C"
JNIEXPORT void JNICALL
Java_com_aispeech_astools_jni_NativeCall_feedBufToJNI(JNIEnv *env, jclass instance, jbyteArray buf_, jint len) {
  jbyte *buf = env->GetByteArrayElements(buf_, NULL);
  decodeBuf(env, (char *) buf, len);
  env->ReleaseByteArrayElements(buf_, buf, 0);
}
